package com.delos.rbac.security;

import com.delos.rbac.error.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 权限验证过滤器
 *
 * @author zouyaowen
 * @date 2020-06-27
 */
@Slf4j
public class TokenFilter extends OncePerRequestFilter {

    private static final String LOGIN = "login";
    private static final String REGISTER = "register";
    private static final String CODE = "code";
    private static final String MESSAGE = "message";

    private final AuthenticationManager authenticationManager;


    public TokenFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        /**
         * 流程如下:
         * 通过SecurityContextHolder.getContext().getAuthentication() != null来判断当前请求是否已经被认证
         * 构造需要认证的TokenAuthentication用户凭证信息
         * 通过AuthenticationManager 验证用户凭证
         * 返回认证后TokenAuthentication信息，并绑定到SecurityContextHolder中
         */
        try {
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                // 认证用户信息构建
                TokenAuthentication authentication = new TokenAuthentication(request, response);
                if (request.getRequestURI().contains(LOGIN) || request.getRequestURI().contains(REGISTER)) {
                    authentication.setAuthenticated(true);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    filterChain.doFilter(request, response);
                    return;
                }
                // 开始认证及授权处理
                authenticationManager.authenticate(authentication);
            }
            filterChain.doFilter(request, response);
        } catch (ServiceException e) {
            //自定义异常信息通过请求对象属性带到authenticationEntryPoint方法中
            request.setAttribute(CODE, e.getCode());
            request.setAttribute(MESSAGE, e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("TokenAuthenticationFilter用户信息处理异常...", e);
            throw new AuthenticationServiceException("TokenAuthenticationFilter用户信息处理异常");
        }
    }
}
